import {Dictionary} from '@/api/config'
import {traveTreeByLevel} from '@/util/common'
import {BASE_API_URL} from '@/store/config'

/** PDFMAKE接口定义
 * 1.所有的单位都是pt pt=px*0.75
 */
export interface PageSizeConfig {
  width: number
  height: number
}
//页面尺寸
export type PageSize = 'A4' | 'A5' | PageSizeConfig
//文档基础样式
export interface DocStyle {
  //字体
  font?: 'SourceHanSansCN' | 'MicrosoftYaHei'
  //字体大小
  fontSize?: number
  //对齐方式
  alignment?: 'center' | 'left' | 'right'| 'justify'
  //加粗
  bold?: boolean
  //斜体
  italics?: boolean
  //颜色
  color?: string
  //背景色
  background?: string
  //外边距
  margin?: number | Array<number>
  //保留空格
  preserveLeadingSpaces?:boolean
  //行高
  lineHeight?:number
  //字间距
  characterSpacing?:number
}

export interface Position {
  x: number
  y: number
}
//基础元素信息
export interface BaseContent {
  //元素定位,没有则按着文档流排布
  absolutePosition?: Position
  relativePosition?: Position
  //分页标识,在当前元素之前还是之后分页
  pageBreak?: 'after' | 'before'
  //公共样式名
  style?: string
}
interface BaseTextContent extends BaseContent {
  //文本数据
  text: string | Array<string>
  //下划线
  decoration?:'underline'|'lineThrough'|'overline'
  //下划线样式
  decorationStyle?:'dashed'|'dotted'|'double'|'wavy'
  //下划线颜色
  decorationColor?:string
  //不换行
  unbreakable?:boolean
}
//文本元素
export type TextContent = BaseTextContent & DocStyle
export interface TextArrayContent extends BaseContent{
  text:Array<TextContent>
}
//图片元素
export interface ImageContent extends BaseContent {
  //图片数据 base64或url 可以取公共图片名
  image: string
  //图片宽高 只设置一个可以按比例缩放
  width?: number
  height?: number
}

//位置元数据
export interface PositionsData{
  horizontalRatio:string
  verticalRatio:number
  left:number
  top:number
  pageNumber:number
}
//回调数据
interface CallCellData{
  //位置元数据 仅在回调时被访问
  positions?:Array<PositionsData>
  //文字分组 仅在回调时被访问
  _inlines?:Array<{height:number, width:number, lineEnd?:boolean}>
  _margin?:[number, number, number, number]
  _maxWidth:number
  _minWidth:number
}
//表格单元格
export interface TableTextContent extends TextContent{
  //合并单元格 注 被合并的单元格应有一个{}空数据
  colSpan?:number
  rowSpan?:number
  //单元格
  fillColor?:string
  //边框
  border?:[boolean, boolean, boolean, boolean]
}
export type CallTableTextContent=TableTextContent&CallCellData
//单元格数据类型
type Cell=TableTextContent | ImageContent | string | number
type CallCell=CallTableTextContent | ImageContent | string | number
//行数据
export type TableLineData=Array<Cell>
//回调行数据
type CallTableLineData=Array<CallCell>
//数据
export type TableBodyData=Array<TableLineData>
type CallTableBodyData=Array<CallTableLineData>
//表格配置
interface TableConfig {
  //表头有几行
  headerRows?: number
  //是否分页保持表头 默认为是
  dontBreakRows?:boolean
  //分页保持表头行数 默认为1
  keepWithHeaderRows?:number
  //表格每列宽度
  widths?: Array<number | string>
  //表格每行高度
  heights?: Array<number | string>
  body: TableBodyData
}
interface CallTableConfig{
  widths:Array<{width:number}>
  body:CallTableBodyData
}
//一般样式
export type TableNormalLayout='noBorders' | 'headerLineOnly' | 'lightHorizontalLines'
//回调Node数据
export interface CallNodeData{
  table:TableConfig&CallTableConfig
}

//单元格边框样式
export interface TableCellStyle{
  //虚线
  dash?:{length:number, space?:number}
}
//回调样式
export interface TableCallLayout{
  //默认边框
  defaultBorder?:boolean
  //填充颜色回调
  fillColor?:(i:number, node:CallNodeData)=>string|null
  //水平边框宽度回调
  hLineWidth?:(i:number, node:CallNodeData)=>number|null
  //垂直边框宽度回调
  vLineWidth?:(i:number, node:CallNodeData)=>number|null
  //水平边框颜色回调
  hLineColor?:(i:number, node:CallNodeData)=>string|null
  //垂直边框颜色回调
  vLineColor?:(i:number, node:CallNodeData)=>string|null
  //水平样式回调
  hLineStyle?:(i:number, node:CallNodeData)=>TableCellStyle|null
  //垂直样式回调
  vLineStyle?:(i:number, node:CallNodeData)=>TableCellStyle|null
  //左边距回调
  paddingLeft?:(i:number, node:CallNodeData)=>number|null
  //右边距回调
  paddingRight?:(i:number, node:CallNodeData)=>number|null
  //上边距回调
  paddingTop?:(i:number, node:CallNodeData)=>number|null
  //下边距回调
  paddingBottom?:(i:number, node:CallNodeData)=>number|null
}
//表格元素
export interface TableContent extends BaseContent {
  //布局 没有边框/表格头边框/高亮边框
  layout?: 'noBorders' | 'headerLineOnly' | 'lightHorizontalLines'|TableCallLayout
  //表格配置
  table: TableConfig
}
//绘图元素 基础配置
export interface BaseCanvasContent extends BaseContent{
  //颜色
  color?:string
  //线条颜色
  lineColor?:string
  //线条粗细
  lineWidth?:number
}
//绘图元素 矩形
export interface RectCanvasContent extends BaseCanvasContent{
  type:'rect',
  //坐标
  x:number
  y:number
  //宽高
  w:number
  h:number
}
//绘图元素 直线
export interface LineCanvasContent extends BaseCanvasContent{
  type:'line',
  //起止点位置
  x1:number,
  y1:number,
  x2:number,
  y2:number,
}
//绘图元素 多段线 推荐画线都使用这个配置
export interface PolylineCanvasContent extends BaseCanvasContent{
  type:'polyline',
  //点集
  points:Array<Position>
  //是否自动闭合
  closePath?:boolean
  //线粗
  lineWidth?:number
  //虚线间隔
  dash?:{length:number}
}
export interface CanvasContent extends BaseContent{
  //元素
  canvas:Array<RectCanvasContent|LineCanvasContent|PolylineCanvasContent>
}

//SVG元素
export interface SVGContent extends BaseContent{
  svg:string
  width?:number
  height?:number
  fit?:[number, number]
}

//分列
interface Columns {
  // 列元素宽度
  width?: string | number
}
//一般元素
export type NormalDocContent=TextContent|TextArrayContent|ImageContent|TableContent|CanvasContent|SVGContent

//列中的栈元素
interface StackContent{
  stack:Array<NormalDocContent|StackContent>
}
export type BaseColumn=Columns&(NormalDocContent|StackContent)
//列元素
export interface ColumnsContent extends BaseContent {
  columns: Array<BaseColumn|Array<BaseColumn>|ColumnsContent>
  //列间距
  columnGap?: number
}

export type DocContent =NormalDocContent|ColumnsContent

//PDF文档配置
export interface DocDefinition {
  //页面尺寸
  pageSize: PageSize
  //页面外边距
  pageMargins?: [number, number, number, number]
  //纸张方向 纵向(默认)或横线
  pageOrientation?: 'portrait' | 'landscape'
  //默认样式
  defaultStyle: DocStyle
  //元素列表
  content: Array<DocContent>
  //公共图片
  images?: Dictionary<string>
  //公共样式
  styles?: Dictionary<DocStyle>
  //页眉元素
  header?:
    | Array<DocContent>
    | ((currentPage: number, pageCount: number) => Array<DocContent>)
  //页脚元素
  footer?:
    | Array<DocContent>
    | ((currentPage: number, pageCount: number) => Array<DocContent>)
}

//生成的PDF文档
export interface PdfDocGenerator {
  //获取PDF的base64数据
  getBase64: (callBack: (base64: string) => void) => void
  //获取PDF的dataUrl
  getDataUrl:(callBack:(dataUrl:string)=>void)=>void
  //获取PDF的ArrayBuffer数据
  getBuffer: (callBack: (buffer: ArrayBuffer) => void) => void
  //直接下载PDF文件
  download: (fileName: string, callBack: () => void) => void
}
export interface PdfMake {
  //字体设置
  fonts: Dictionary<Dictionary<string>>
  createPdf: (doc: DocDefinition) => PdfDocGenerator
}

//获取文字高度
export const getTextHeight = (doc:DocDefinition, textBase:TextContent):number|undefined => {
  if (textBase.lineHeight) {
    return textBase.lineHeight
  }
  else if (textBase.fontSize) {
    return textBase.fontSize
  }
  else if (textBase.style && doc.styles && doc.styles[textBase.style] && doc.styles[textBase.style].fontSize) {
    return doc.styles[textBase.style].fontSize
  }
  else if (doc.defaultStyle.fontSize) {
    return doc.defaultStyle.fontSize
  }
}
//获取该行中表格文字需要的上边距
export const getVCellMarginTop = (table:CallTableConfig, i:number):Array<number>|false => {
  const lineData = table.body[i]
  const {widths} = table
  //最大高度
  let maxHeight = 10
  const heights:Array<number> = []
  const lineHeights:Array<number> = []
  for (let i = 0, l = lineData.length; i < l; i++) {
    const cell = lineData[i] as CallTableTextContent
    const inlines = cell._inlines
    if (!inlines || !inlines.length) {
      heights.push(maxHeight)
      continue
    }
    const maxWidth = widths[i].width
    const lineHeight = inlines[0].height
    lineHeights.push(lineHeight)
    let width = 0
    let lineNum = 1
    inlines.forEach((inline) => {
      width += inline.width
      if (width > maxWidth || inline.lineEnd) {
        width -= maxWidth
        lineNum++
      }
    })
    const height = lineNum * lineHeight
    maxHeight = maxHeight < height ? height : maxHeight
    heights.push(height)
  }
  const margins:Array<number> = []
  heights.forEach((height) => {
    const marginTop = (maxHeight - height) / 2
    margins.push(marginTop)
  })
  return margins
}

//数据
interface TreeData{
  label:string
  value:boolean
  deep?:number
  children?:Array<TreeData>
}

interface Treeconfig{
  boxSize:number
  boxMargin:number
  nodeHeight:number
  parentOutLine:number
  childrenLine:number
}

/**
 * 生成树形数据对应的列元素
 * @param data 数据
 * @param startPt 起始位置
 */
export const createColByTree = (data:Array<TreeData>, startPt?:Position, widths?:Array<number>, config?:Treeconfig) => {
  //勾选框svg
  const notCheck = '<svg id="勾_空_" data-name="勾（空）" xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><g id="矩形_1284" data-name="矩形 1284" fill="#fff" stroke="#000" stroke-width="2"><rect width="40" height="40" stroke="none"/><rect x="1" y="1" width="38" height="38" fill="none"/></g></svg>'
  const checked = '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="40" height="40" viewBox="0 0 40 40"><defs><clipPath id="clip-path"><rect id="矩形_1282" data-name="矩形 1282" width="30" height="30" stroke="#000" stroke-width="2"/></clipPath></defs><g id="勾" transform="translate(-431 -1003)"><g id="组件_1_7" data-name="组件 1 – 7" transform="translate(431 1003)"><g id="矩形_1282-2" data-name="矩形 1282" fill="#fff" stroke="#000" stroke-width="2"><rect width="40" height="40" stroke="none"/><rect x="1" y="1" width="38" height="38" fill="none"/></g></g><g id="组件_1_8" data-name="组件 1 – 8" transform="translate(436 1008)"><g id="蒙版组_3" data-name="蒙版组 3" clip-path="url(#clip-path)"><path id="勾-2" data-name="勾" d="M30,3.191s-4.8,1.964-10.4,9.818A77.578,77.578,0,0,0,11.2,27,31.884,31.884,0,0,0,0,14.482L4.2,9.573a26.9,26.9,0,0,1,6.4,9.082S17.2,6.136,30,0V3.191Zm0,0" transform="translate(0 3)"/></g></g></g></svg>'
  //勾选框大小
  const boxSize = config && config.boxSize !== undefined ? config.boxSize : 10
  //勾选框右边距
  const boxMargin = config && config.boxMargin !== undefined ? config.boxMargin : 4
  //节点高度
  const nodeHeight = config && config.nodeHeight !== undefined ? config.nodeHeight : 14
  //节点间距 实际间距还需要看子树有多高
  const nodeMargin = nodeHeight - boxSize
  const lineHeight = Math.max(boxSize, nodeHeight) + nodeMargin * 2
  const parentOutLine = config && config.parentOutLine !== undefined ? config.parentOutLine : 10
  const childrenLine = config && config.childrenLine !== undefined ? config.childrenLine : 15
  //层次栈
  const levelStack:Array<Array<TreeData>> = []
  const visiter = (node:TreeData, lv:number) => {
    let current = levelStack[lv]
    if (!current) {
      current = []
      levelStack.push(current)
    }
    current.push({...node})
  }
  traveTreeByLevel(data, visiter)
  //最多的一层
  let maxLvNum = 0
  levelStack.forEach((level) => {
    maxLvNum = maxLvNum < level.length ? level.length : maxLvNum
  })
  const maxHeight = maxLvNum * lineHeight
  const columns:ColumnsContent = {columns:[], columnGap:0, relativePosition:startPt}
  levelStack.forEach((level, index) => {
    const width = widths && widths[index] ? widths[index] : 100
    const stack:BaseColumn = {stack:[], width}
    level.forEach((node) => {
      //上边距
      const marginTop = node.children && node.children.length > 1 ? ((node.children.length - 1) * lineHeight / 2 + boxSize / 2 - 2) : nodeMargin
      //文本和勾选框的左边距
      let textMarginLeft = boxSize + boxMargin
      let checkMarginLeft = 0
      const lineStack:StackContent = {stack:[]}
      //线条
      if (node.children && node.children.length) {
        const canavs:CanvasContent = {canvas:[], relativePosition:{x:parentOutLine, y:marginTop + nodeHeight / 2}}
        //出线
        canavs.canvas.push({type:'line', x1:width - parentOutLine, y1:0, x2:width, y2:0})
        const vHeight = (lineHeight * (node.children.length - 1))
        for (let i = 0; i < node.children.length; i++) {
          const y = i * lineHeight - vHeight / 2
          canavs.canvas.push({type:'line', x1:width, x2:width + parentOutLine, y1:y, y2:y})
        }
        //大于1显示竖线
        if (node.children.length > 1) {
          canavs.canvas.push({type:'line', x1:width, y1:-vHeight / 2, x2:width, y2:vHeight / 2})
        }
        lineStack.stack.push(canavs)
      }
      if (index !== 0) {
        checkMarginLeft += parentOutLine + childrenLine
        textMarginLeft += parentOutLine + childrenLine
      }
      lineStack.stack.push({svg:node.value ? checked : notCheck, width:boxSize, relativePosition:{x:checkMarginLeft, y:marginTop + 2}})
      lineStack.stack.push({text:node.label, margin:[textMarginLeft, marginTop + 1, 0, marginTop + 1], fontSize:8})
      stack.stack.push(lineStack)
    })
    columns.columns.push(stack)
  })
  return {columns, maxHeight}
}

import pdfMake from 'pdfmake/build/pdfmake'
const pdf = pdfMake as PdfMake
//设置默认字体
const setFonts = () => {
  pdf.fonts = {
    SourceHanSansCN: {
      normal: BASE_API_URL + '/resources/SourceHanSerifCN-Regular.otf',
      bold: BASE_API_URL + '/resources/SourceHanSerifCN-Bold.otf',
      italics: BASE_API_URL + '/resources/SourceHanSerifCN-Regular.otf',
      bolditalics: BASE_API_URL + '/resources/SourceHanSerifCN-Regular.otf'
    },
    MicrosoftYaHei: {
      normal: BASE_API_URL + '/resources/MicrosoftYaHei-Normal.ttf',
      bold: BASE_API_URL + '/resources/MicrosoftYaHei-Bold.ttf',
      italics: BASE_API_URL + '/resources/MicrosoftYaHei-Normal.ttf',
      bolditalics: BASE_API_URL + '/resources/MicrosoftYaHei-Normal.ttf'
    }
  }
}


export const pageSizeHash: Dictionary<PageSizeConfig> = {
  '5cm*3cm': {width: 142, height: 85},
  '5cm*4cm': {width: 142, height: 113},
  '8cm*5cm': {width: 227, height: 142}
}

//根据文档下载pdf
export const downPdf = (
  doc: DocDefinition,
  fileName: string,
  useFont?: boolean
): Promise<null> => {
  if (useFont) setFonts()
  return new Promise((resolve) => {
    const pdfDocGenerator = pdf.createPdf(doc)
    pdfDocGenerator.download(fileName, () => {
      resolve(null)
    })
  })
}

//根据文档获取pdf的ArrayBuffer数据
export const getPdfArraybuffer = (
  doc: DocDefinition,
  useFont?: boolean
): Promise<ArrayBuffer> => {
  if (useFont) setFonts()
  return new Promise((resolve) => {
    const pdfDocGenerator = pdf.createPdf(doc)
    pdfDocGenerator.getBuffer((buffer) => {
      resolve(buffer)
    })
  })
}

//根据文档获取pdf的Base64数据
export const getPdfBase64 = (
  doc: DocDefinition,
  useFont?: boolean
): Promise<string> => {
  if (useFont) setFonts()
  return new Promise((resolve) => {
    const pdfDocGenerator = pdf.createPdf(doc)
    pdfDocGenerator.getBase64((base64) => {
      resolve(base64)
    })
  })
}

//根据文档获取pdf的DataUrl数据
export const getPdfDataUrl = (doc:DocDefinition, useFont?:boolean):Promise<string> => {
  if (useFont) setFonts()
  return new Promise((resolve, reject) => {
    const pdfDocGenerator = pdf.createPdf(doc)
    pdfDocGenerator.getDataUrl((dataUrl) => {
      resolve(dataUrl)
    })
  })
}